package com.xhh.microfin.dispatch.service.process;

import com.xhh.microfin.dispatch.model.entity.NextNodes;
import com.xhh.microfin.dispatch.model.entity.ProcessNode;
import com.xhh.microfin.dispatch.model.entity.Task;
import com.xhh.microfin.dispatch.model.enums.TaskStatusEnum;
import com.xhh.microfin.dispatch.model.mapper.ProcessNodeMapper;
import com.xhh.microfin.dispatch.model.mapper.TaskMapper;
import com.xhh.microfin.dispatch.model.mapper.extend.NextNodesExtendMapper;
import com.xhh.microfin.dispatch.service.process.impl.ProcessContext;
import com.xhh.microfin.dispatch.service.process.impl.Var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @Author: 003035
 * @Date: 2019/1/16
 */
@Service
public abstract class CommonTaskImpl implements ITask {

    @Autowired
    private ProcessNodeMapper processNodeMapper;

    @Autowired
    private IProcess process;

    @Autowired
    private NextNodesExtendMapper nextNodesExtendMapper;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private ApplicationContext applicationContext;


    @Override
    public boolean active(Task task, ProcessContext context) {

        if (TaskStatusEnum.ACTIVE.getCode().equals(task.getStatus())) {
            return true;
        }

//        canBeActive(task, context);

        boolean b = busOnActive(context);
        if (b) {
            task.setStatus(TaskStatusEnum.ACTIVE.getCode());
            taskMapper.updateByPrimaryKey(task);
        }
        return b;
    }

    protected boolean busOnActive(ProcessContext context){
        return true;
    }


    @Override
    public void process(Task task) {

        ProcessContext processContext = process.prepareProcessContext(task);
        if (active(task,processContext) && done(task,processContext)) {
            next(task,processContext);
        }
    }

    @Override
    public boolean canBeDone(Task task,ProcessContext context) {

        Integer nodeId = task.getNodeId();
        ProcessNode processNode = processNodeMapper.selectByPrimaryKey(nodeId);
        String doneCondition = processNode.getDoneCondition();
        return getResult(doneCondition, buildEvaluationContext(context));
     }

    @Override
    public boolean canBeActive(Task task,ProcessContext context) {
        Integer nodeId = task.getNodeId();
        ProcessNode processNode = processNodeMapper.selectByPrimaryKey(nodeId);
        String activeCondition = processNode.getActiveCondition();
        return getResult(activeCondition, buildEvaluationContext(context));
    }

    @Override
    public boolean canBeSelect(Task task,ProcessContext context) {
        Integer nodeId = task.getNodeId();
        ProcessNode processNode = processNodeMapper.selectByPrimaryKey(nodeId);
        String selectCondition = processNode.getSelectCondition();
        return getResult(selectCondition, buildEvaluationContext(context));
    }

    private boolean getResult(String condition, EvaluationContext context) {
        try {
            Boolean value = getExpressionParser().parseExpression(condition).getValue(context, Boolean.class);
            return value;
        } catch (EvaluationException e) {
            return false;
        }
    }


    @Override
    public EvaluationContext buildEvaluationContext(ProcessContext processContext) {
        EvaluationContext context = new StandardEvaluationContext();
        for (String key : processContext.getVars().keySet()) {
            context.setVariable(key, processContext.getVar(key));
        }

        return context;
    }


    @Override
    public void next(Task task,ProcessContext context) {

        Integer nodeId = task.getNodeId();
        ProcessNode processNode = processNodeMapper.selectByPrimaryKey(nodeId);
        List<NextNodes> nextNodes = nextNodesExtendMapper.selectByNodeId(processNode.getId());
        nextNodes.forEach(nextNode -> {
            Task temp = new Task();
            temp.setNodeId(nextNode.getNextNodeId());
            if (canBeSelect(temp, context)) {
                ProcessNode nextProcessNode = processNodeMapper.selectByPrimaryKey(nextNode.getNextNodeId());
                Task begin = begin(nextProcessNode, task.getProcessInstanceId());
                ITask itask = applicationContext.getBean(nextProcessNode.getCode(), ITask.class);
                itask.process(begin);
            }
        });
    }

    @Override
    public Task begin(ProcessNode processNode, Integer processInstanceId) {
        Task task = new Task();
        task.setProcessId(processNode.getProcessId());
        task.setProcessInstanceId(processInstanceId);
        task.setNodeId(processNode.getId());
        task.setStatus(TaskStatusEnum.BEGIN.getCode());
        taskMapper.insert(task);
        return task;
    }

    @Override
    public boolean done(Task task,ProcessContext context) {

        boolean b = canBeDone(task,context);
        if (!b) {
            return false;
        }

        if (!doneBusiness(task, context)) {
            return false;
        }

        task.setStatus(TaskStatusEnum.DONE.getCode());
        taskMapper.updateByPrimaryKey(task);
        return true;
    }

    protected boolean doneBusiness(Task task,ProcessContext context) {
        return true;
    }
}
